package com.sbc.auth.toolkit;

import com.alibaba.fastjson2.JSON;
import com.sbc.convention.exception.ServiceException;
import com.sbc.convention.pojo.dto.UserInfoDTO;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: JWT 工具类
 * @author: songbaicheng
 * @create: 2024-03-23 22:32
 **/
@Slf4j
public class JWTUtil {
    private static final long EXPIRATION = 86400L;
    public static final String TOKEN_PREFIX = "Bearer ";
    /**
     * 签发者
     */
    public static final String ISS = "12306";
    public static final String SECRET = "SecretKey039245678901232039487623456783092349288901402967890140939827";

    /**
     * 生成用户 Token
     *
     * @param userInfo 用户信息
     * @return 用户访问 Token
     */
    public static String generateAccessToken(UserInfoDTO userInfo) {
        Map<String, Object> claims = generateClaims(userInfo);
        return TOKEN_PREFIX + generateToken(claims);
    }

    /**
     * 验证用户 Token 的有效性
     *
     * @param jwtToken 用户访问 Token
     * @return 是否有效
     */
    public static boolean validateToken(String jwtToken) {
        if (StringUtils.hasText(jwtToken)) {
            String actualJwtToken = jwtToken.replace(TOKEN_PREFIX, "");
            try {
                Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(actualJwtToken).getBody();
                return validateTokenExpiration(claims);
            } catch (ExpiredJwtException ignored) {
                log.warn("JWT Token 已过期");
            } catch (Exception ex) {
                log.error("JWT Token 解析失败！", ex);
            }
        }
        return false;
    }

    /**
     * 解析用户 Token
     *
     * @param jwtToken 用户访问 Token
     * @return 用户信息
     */
    public static UserInfoDTO parseJwtToken(String jwtToken) {
        if (StringUtils.hasText(jwtToken)) {
            String actualJwtToken = jwtToken.replace(TOKEN_PREFIX, "");
            try {
                Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(actualJwtToken).getBody();
                if (validateTokenExpiration(claims)) {
                    return retrieveUserInfoFromClaims(claims);
                }
            } catch (ExpiredJwtException ignored) {
            } catch (Exception ex) {
                log.error("JWT Token 解析失败！", ex);
                throw new ServiceException("JWT Token 解析失败！");
            }
        }
        return null;
    }

    private static Map<String, Object> generateClaims(UserInfoDTO userInfo) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", userInfo.getUserId());
        claims.put("username", userInfo.getUsername());
        claims.put("realName", userInfo.getRealName());
        return claims;
    }

    private static String generateToken(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(new Date())
                .setIssuer(ISS)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION * 1000))
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
    }

    private static boolean validateTokenExpiration(Claims claims) {
        Date expiration = claims.getExpiration();
        return expiration != null && expiration.after(new Date());
    }

    private static UserInfoDTO retrieveUserInfoFromClaims(Claims claims) {
        String subject = claims.getSubject();
        return JSON.parseObject(subject, UserInfoDTO.class);
    }
}
